/**
 * 接口
 * 接口一方面可以在面向对象的编程表示行为的抽象，另外也可能用来描述对象的形状
 */
//描述对象形式
/* interface Speakable{
    speak():void;//说话
    name?:string;//名字 ?:表示此属性可有可无
} */
/**
 不能将类型“{ speak(): void; name: string; age: number; }”分配给类型“Speakable”。
  对象字面量只能指定已知属性，但是“age”不在类型“Speakable”中。
 */
/* let p1:Speakable={
    speak(){},
    name:'person',
    //age:18 不能添加未定义的属性
} */
/* 
//行为的抽象
interface Speakable{
    speak():void;//说话
}
interface Eatable{
    eat():void;//吃
}
//类可以实现接口，意味着类里要具体实现接口里定义的方法
//一个类可以实现多个接口
class Person implements Speakable,Eatable{
    speak(){}
    eat(){}
}

//任意属性
interface Person2{
    name:string;
    age:number;
    //当你无法事先预告新的属性的时候，可以使用任意属性
    [propName:string]:any
}
let p2:Person2={
    name:'zhangsan',
    age:18,//赋值的对象除了肯定有name和age外，其它的属性不固定
    home:'北京'
} */

//void不是不能有返回值吗 上面接口的speak方法不会限制对象里面的吗
/* interface Speakable{
    speak():void;
}
let p3:Speakable={
    speak(){}
} */
//接口的继承
/* interface Speakable{
    speak():void
}
interface SpeakChinese extends Speakable{
    speakChinese():void
}
//类型“Person”缺少类型“SpeakChinese”中的以下属性: speakChinese, speak
class Person implements SpeakChinese{
    speakChinese(): void {
        console.log('speakChinese')
    }
    speak(): void {
       console.log('speak')
    }
} */
//readonly
//用readonly定义只读属性可以避免由于多人协作或项目比较复杂的原因导致值被重写
/* interface Person{
    readonly id:number;
    name:string;
}
let tom:Person = {
    id:1,
    name:'zhangsan'
}
//无法为“id”赋值，因为它是只读属性
//tom.id = 2;
tom.name = 'lisi';
 */
//接口还可以对函数进行约束
//可以对方法的参数和返回值进行约束
/* 
interface Discount{
    (price:number):number;
}
//不能将类型“() => void”分配给类型“Discount”
//不能将类型“void”分配给类型“number”
let cost:Discount = function(price:number){
    return price*.8;
}
//可索引接口
//接口还可以对数组和对象进行约束 
interface UserInterface{
    [index:number]:string;
}
let arr:UserInterface=['a','b','c']
arr[0]='a';
arr[1]='b';
arr[2]='c';

interface UserInterface2{
    [prop:string]:string;
}
let obj2:UserInterface2={
    name:'zhangsan',
    //不能将类型“number”分配给类型“string”
    age:18
} */
//对类进行约束
/* interface Speakable{
    name:string;
    speak(words:string):void
}
class Dog implements Speakable{
    name: string;
    speak(words: string): void {
        throw new Error("Method not implemented.");
    }
} */
//在TS中，可以对interface描述类
//同时也可以使用interface特殊关键字new来描述类的构造函数
/* class Animal{
    name:string
    constructor(name:string){
        this.name = name;
    }
}
//具有能接受name参数构造函数的一个类
interface WithNameCLass{
    //此处用来约束类的构造函数
    new (name:string,age:number):Animal
}

function createAnimal(clazz:WithNameCLass,name:string,age:number){
    return new clazz(name,age);
}
let result2 = createAnimal(Animal,'dog',16);
console.log(result2.name)
 */
/**
抽象类VS接口
不同的类之间公有的属性或方法，可以抽象为一个接口  interface
而抽象类是供其它子类继承的基类，抽象类不能实例化，抽象类中的方法必须子类中实现
抽象类本质是提供一个无法被实例化的类，其中能够实现方法和初始属性
而接口仅能描述类型，不能提供方法的实现，也不能为属性初始化
一个类可以继承一个类或抽象类，只能继承自一个类，可以同时实现多个接口
抽象类也可以实现接口
 */
/* abstract class Animal{
    name:string
    constructor(name:string){
        //抽象类中可以初始化属性
        this.name = name;
    }
    //也可以定义方法，也可以实现方法
    print(){console.log('Animal')}
    //抽象方法只有方法名、参数的名称和类型以及返回值类型
    abstract speak(words:string):void;
}
interface Flyable{
    //接口里不能实现方法和初始化属性
    fly():void
}
//一个子类能且只能继承自一个父类
//非抽象类“Duck”尚未实现继承自“Animal”类的抽象成员“speak”。
class Duck extends Animal implements Flyable{
    fly(): void {
        console.log('fly')
    }
    speak(words: string): void {
      console.log('speak',words)
    }
    
} */

//用接口来约束类的构造函数
//可以使用interface中特殊的new关键字来描述类的构造函数
/* class Animal{
    name:string;
    age:number;
    constructor(name:string){
        //this.name = name;
        this.age = age;
    }
}
//小括号前面添加new关键字表示用来约束的类的构造函数
interface WithParameterNameClass{
    new (name:string):any
}
//不能将类型“typeof Animal”分配给类型“WithParameterNameClass”
//参数“age”和“name” 的类型不兼容。
//不能将类型“string”分配给类型“number”
let a2:WithParameterNameClass=Animal; */
///如果用接口约束一个函数
/* interface Discount{
    (price:number):number
}
//类型 "(price: number) => number" 中缺少属性 "age"，
//但类型 "Discount" 中需要该属性。ts(2741)
let cost:Discount=(price:number)=>{
    return price*.8;
} */